﻿// ===============================================================================
// Route.cs
// EOS Web Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Contracts.Web.Routing
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using EOS.Framework.Tools;

    /// <summary>
    /// Specifies a route. Whenever a url matches to the url specified in
    /// a route, all requests to this url will be processed by route handlers.
    /// </summary>
    /// <example>
    /// A route is used for extracting parameters from URLs. If you want to make a shop
    /// system for example and you have urls like
    /// <c>http://www.myshop.net/Products/1/MySoftware/</c>, you can define a route this
    /// way, that the router extracts the parameters from the url a returns a list
    /// of all values.
    /// <code>
    /// // Use the following code to define a route for the example described above.
    /// Route route = new Route("/Products/[ProductId]/[ProductName]/", typeof(MyHandler));
    /// // If the url has a matching format, the router will return the parameters ProductId
    /// // and ProductName, in the example above with the values '1' and 'MySoftware'.
    /// </code>
    /// </example>
    public class Route : IEquatable<Route>
    {
        #region Properties

        /// <summary>
        /// The priority for the current route.
        /// </summary>
        private RoutePriority _priority = RoutePriority.Medium;

        /// <summary>
        /// Gets or sets the priortiy for the current route.
        /// </summary>
        /// <value>The priority.</value>
        public RoutePriority Priority
        {
            get { return _priority; }
            set { _priority = value; }
        }

        /// <summary>
        /// The type of the class, who will handle the route.
        /// </summary>
        private Type _handler;

        /// <summary>
        /// Gets or sets the type of the route handler.
        /// </summary>
        /// <value>The type of the class, who will handle the route.
        public Type Handler
        {
            get { return _handler; }
            set { _handler = value; }
        }

        /// <summary>
        /// The url, where this route will be mapped to.
        /// </summary>
        private string _url;

        /// <summary>
        /// Gets or sets the URL.
        /// </summary>
        /// <value>The url, where this route will be mapped to.</value>
        [SuppressMessage("Microsoft.Design", "CA1056", Justification = "Uri class cannot handle this kind of urls")]
        public string Url
        {
            
            get { return _url; }
            set { _url = value; }
        }

        /// <summary>
        /// A list of constraints, who define the format of the route parameters.
        /// </summary>
        private RouteValueDictionary _constraints;

        /// <summary>
        /// Gets the constraints.
        /// </summary>
        /// <value>The constraints, who define the format of the route parameters.
        /// Cannot be null (Nothing in Visual Basic).</value>        
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is
        /// null (Nothing in Visual Basic).</exception>
	    public RouteValueDictionary Constraints
	    {
		    get 
            {
                if (_constraints == null)
                {
                    _constraints = new RouteValueDictionary();
                }
                return _constraints;
            }
	    }

        /// <summary>
        /// The default values for the route parameters.
        /// </summary>
        private RouteValueDictionary _defaults;

        /// <summary>
        /// Gets or sets the default values.
        /// </summary>
        /// <value>The default values for the route parameters. Cannot be null
        /// (Nothing in Visual Basic).</value>        
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is
        /// null (Nothing in Visual Basic).</exception>
        public RouteValueDictionary Defaults
        {
            get 
            {
                if (_defaults == null)
                {
                    _defaults = new RouteValueDictionary();
                }
                return _defaults; 
            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="Route"/> class.
        /// </summary>
        public Route()
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Route"/> class with
        /// an URL and a reference to a class, which handles the route.
        /// </summary>
        /// <param name="url">The URL for this route.</param>
        /// <param name="handler">The type of the route handler.</param>
        [SuppressMessage("Microsoft.Design", "CA1054", Justification = "Uri class cannot handle this kind of urls")]
        public Route(string url, Type handler)
            : this(url, null, handler)
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Route"/> class with
        /// an URL and a reference to a class, which handles the route and a list of default values.
        /// </summary>
        /// <param name="url">The URL for this route.</param>
        /// <param name="defaults">Collection of default values for route parameters.</param>
        [SuppressMessage("Microsoft.Design", "CA1054", Justification = "Uri class cannot handle this kind of urls")]
        public Route(string url, RouteValueDictionary defaults, Type handler)
            : this(url, defaults, null, handler)
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Route"/> class with
        /// an URL, a reference to a class, which handles the route, a collection of default values and
        /// constraints, who define the format of route parameters.
        /// </summary>
        /// <param name="url">The URL for this route.</param>
        /// <param name="defaults">Collection of default values for route parameters.</param>
        /// <param name="constraints">A collection of constraints, who define the format of route parameters.</param>
        /// <param name="handler">The type of the route handler.</param>
        [SuppressMessage("Microsoft.Design", "CA1054", Justification = "Uri class cannot handle this kind of urls")]
        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, Type handler)
            : this(url, defaults, constraints, RoutePriority.Medium, handler)
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Route"/> class with
        /// an URL, a reference to a class, which handles the route, a collection of default values,
        /// constraints, who define the format of route parameters and a priority for the current route.
        /// </summary>
        /// <param name="url">The URL for this route.</param>
        /// <param name="defaults">Collection of default values for route parameters.</param>
        /// <param name="constraints">A collection of constraints, who define the format of route parameters.</param>
        /// <param name="priority">Defines the priority of this route.</param>
        /// <param name="handler">The type of the route handler.</param>
        [SuppressMessage("Microsoft.Design", "CA1054", Justification = "Uri class cannot handle this kind of urls")]
        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RoutePriority priority, Type handler)
        {
            this._constraints = constraints;
            this._defaults = defaults;
            this._handler = handler;
            this._priority = priority;
            this._url = url;
        }

        #region IEquatable<Route> Members

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is 
        /// equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with 
        /// the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            return Equals(obj as Route);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Route other)
        {
            if (other == null)
            {
                return false;
            }

            bool result = true;

            if (result && Object.Equals(_handler, other._handler))
            {
                result = false;
            }

            if (result && Object.Equals(_priority, other._priority))
            {
                result = false;
            }

            if (result && Object.Equals(_url, other._url))
            {
                result = false;
            }

            if (result && Extensions.IsEqual(_constraints, other._constraints))
            {
                result = false;
            }

            if (result && Extensions.IsEqual(_defaults, other._defaults))
            {
                result = false;
            }

            return result;
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            int hashCode = _priority.GetHashCode();

            if (_handler != null)
            {
                hashCode ^= _handler.GetHashCode();
            }
            if (_url != null)
            {
                hashCode ^= _url.GetHashCode(); 
            }
            if (_constraints != null)
            {
                hashCode ^= _constraints.CreateHashCode();
            }
            if (_defaults != null)
            {
                hashCode ^= _defaults.CreateHashCode();
            }

            return hashCode;
        }

        #endregion
    }
}
